import { messages, EventUtils, trim } from "share/common";
import React from "react";
import { Button, Row, Col, Spin, Popover } from "antd";
import "styles/components/template/comment/comment.scss";
import { message } from "antd/lib/index";

/**
 * ps list
 * 1.删除的时候名字@符整块一起删除  已解决 塞入节点的contentEditable设置为false
 * 2.只支持纯文本输入，不允许粘贴图片啥的  部分解决 user-modify 兼容性问题
 * 3.去除写死的@人之后，改用弹框发现焦点会变，获取到的光标位置不准了 已解决 用range.cloneRange()
 * 4.如果使用@按钮，而不是键盘输入的，需要确保评论区被focus 待开发
 * 5.placeholder不能过长，如果超过mention-block的宽度就不行
 */

class Comment extends React.Component {
  state = {
    showSelector: false,
    bookMark: undefined, //保存失焦前的range现场
    isBtnShow: false, //是否是按钮打开的弹框
    showPlaceHolder: true, //是否展示placeHolder
    content: [], // 存放富文本的内容
    caretPos: {},
    dataList: [], // 存放搜索出来的数据
    isLoading: false,
  };
  mentionList = []; // 用来存放@的人
  caretBackup = null; // 缓存光标对象
  needSearch = false; // 是否需要搜索
  keyWord = ""; // 模糊匹配的关键字

  componentDidMount() {
    if (!this.props.hasQuickReplayWrapper) {
      this.insertText(this.props.value);
    }
    EventUtils.addHandler(this.getCommentRef(), "keyup", this.handleKeyUp);
    EventUtils.addHandler(this.getCommentRef(), "keydown", this.handleKeyDown);
    EventUtils.addHandler(document, "click", this.handleDocClick);
  }
  componentWillReceiveProps(nextProps) {
    if (nextProps.value !== this.props.value && typeof nextProps.value === "string") {
      this.insertText(nextProps.value);
      return;
      let originValue = this.props.value;
      let nextValue = nextProps.value;
      if (typeof originValue === "object") {
        originValue = originValue.text;
        nextValue = trim(nextValue) + ",";
      }
      if (originValue !== nextValue) {
        this.insertText(nextProps.value);
      }
    }
  }

  componentWillUnmount() {
    EventUtils.removeHandler(this.getCommentRef(), "keyup", this.handleKeyUp);
    EventUtils.removeHandler(this.getCommentRef(), "keydown", this.handleKeyDown);
    EventUtils.removeHandler(document, "click", this.handleDocClick);
  }

  handleDocClick = (e) => {
    this.closeSelector();
  };

  handleKeyDown = (e) => {
    let { maxSize } = this.props;
    if (this.getTextLength() >= maxSize) {
      if (e.keyCode !== 8) {
        e.preventDefault();
        e.stopPropagation();
        this.showMaxSizeTip();
      }
    }
  };

  showMaxSizeTip = () => {
    if (this.timer) return;
    this.timer = setTimeout(() => {
      clearTimeout(this.timer);
      this.timer = null;
    }, 3000);
    message.error(
      messages("common.max.characters.length", {
        arg1: this.props.maxSize,
      }) /*最多输入{arg1}个字符*/
    );
  };

  // debouce接口请求
  getDataWithDebounce = (keyWord) => {
    if (this.timer) {
      clearTimeout(this.timer);
    }
    this.timer = setTimeout(() => {
      this.getData(keyWord).then((res) => {
        clearTimeout(this.timer);
        this.timer = null;
      });
    }, 500);
  };

  getService = (keyWord) => {
    return this.props.service(keyWord);
  };

  getData = (keyWord) => {
    this.keyWord = keyWord;
    this.setState({
      showSelector: true,
      isLoading: true,
    });
    return this.getService(keyWord)
      .then((res) => {
        if (keyWord !== this.keyWord) return;
        let { data = [] } = res;
        let hasData = data.length > 0;
        this.setState({
          showSelector: hasData,
          dataList: data,
          needSearch: hasData,
          isLoading: false,
        });
      })
      .catch((e) => {
        this.setState({
          showSelector: false,
          dataList: [],
          needSearch: false,
          isLoading: false,
        });
      });
  };

  // 插入文档
  insertText = (text) => {
    if (!text || typeof text !== "string") {
      return;
    }
    let { maxSize } = this.props;
    let length = text.length;
    let currentLength = this.getTextLength();

    if (currentLength + length > maxSize) {
      this.showMaxSizeTip();
      return;
    }
    if (text === "@") {
      if (currentLength === 0) {
        text = ` @`;
      }
      let textNode = document.createTextNode(text);
      this.insertNodeToRange(textNode, true);
      setTimeout(() => {
        let sel = this.getSelection();
        if (sel.extend) {
          sel.extend(textNode, text.length);
        }
        this.handleCaret({ focusNode: textNode });
      }, 10);
    } else {
      text = trim(text);
      text = `${text},`;
      let textNode = document.createTextNode(text);
      this.insertNodeToRange(textNode);
    }

    this.setState({
      showPlaceHolder: false,
    });
    this.onChange();
  };

  //获取comment block的dom元素，兼容IE，这里目前是需要兼容childNodes，别的属性还得再看
  getCommentRef = () => {
    //this.ref和findDOMNode一个是虚拟dom，一个是真实dom
    let commentRef = this.refs[`comment`];
    return commentRef || document.getElementById("comment-block");
  };
  // 获取富文本框内的元素节点内容
  getTotalContent = (focusNode, size) => {
    let result = "";
    let parentNode = focusNode.parentNode;
    let childNodes = parentNode.childNodes;
    [].find.call(childNodes, (node) => {
      if (node === focusNode) {
        if (node.nodeType === 1) {
          result += node.innerText.substring(0, size);
        } else if (node.nodeType === 3) {
          result += node.nodeValue.substring(0, size);
        }
        return true;
      } else {
        if (node.nodeType === 1) {
          result += node.innerText;
        } else if (node.nodeType === 3) {
          result += node.nodeValue;
        }
        return false;
      }
    });
    return result;
  };
  // 获取光标的位置
  getCaretPosition = (commentDom, selection) => {
    let tempDom = commentDom.cloneNode();
    tempDom.innerHTML = this.getTotalContent(selection.focusNode, selection.focusOffset);
    tempDom.style.position = "absolute";
    tempDom.style.zIndex = "-1";
    tempDom.style.width = "100%";
    let spanDom = document.createElement("span");
    tempDom.appendChild(spanDom);
    commentDom.parentNode.appendChild(tempDom);
    let offsetTop = spanDom.offsetTop - commentDom.scrollTop;
    let offsetLeft = spanDom.offsetLeft;
    commentDom.parentNode.removeChild(tempDom);
    return {
      offsetTop,
      offsetLeft,
    };
  };
  getSelection = () => {
    return window.getSelection() || document.getSelection();
  };
  // 获取输入的关键字
  getKeyWord = (selection) => {
    let focusNode = selection.focusNode;
    let content = focusNode.nodeValue || focusNode.innerText;
    let caretOffset = selection.focusOffset;
    let computedContent = content.slice(0, caretOffset);
    let keywordAry = computedContent.split("@");
    // 处理keyup触发shift键的情况
    if (keywordAry.length < 2) {
      return "no_notice_flag";
    }
    let keyWord = keywordAry[keywordAry.length - 1] || "";
    return keyWord;
  };
  // 光标相关处理
  handleCaret = ({ selection, isShiftKey = false, focusNode = null } = {}) => {
    selection = selection || this.getSelection();
    let commentDom = this.getCommentRef();
    let keyWord = this.getKeyWord(selection);
    // 处理shft键触发事件的情况
    if (keyWord === "no_notice_flag") {
      return;
    }
    let { offsetTop, offsetLeft } = this.getCaretPosition(commentDom, selection);
    this.textNode = focusNode || selection.focusNode;
    this.needSearch = true;
    this.setState({
      caretPos: {
        top: offsetTop + 30,
        left: offsetLeft - 20,
      },
    });
    this.getDataWithDebounce(keyWord);
  };
  // keydown事件处理
  handleKeyUp = (e) => {
    let { needSearch } = this;
    this.setState({
      showPlaceHolder: this.judgeHasContent(),
    });
    let sel = this.getSelection();
    this.caretBackup = sel.getRangeAt(0);
    //不让输入@，@在选完人后和人名包在一个块里，便于样式特殊以及删除操作
    console.log(
      "handleKeyUp e.keyCode:",
      e.keyCode,
      ", e.key:",
      e.key,
      ", e.shiftKey:",
      e.shiftKey,
      ",needSearch:",
      needSearch
    );
    if (e.keyCode === 50 || e.keyCode === 64 || e.key === "@" || needSearch) {
      e.stopPropagation();
      e.preventDefault();
      this.handleCaret();
    } else if (e.keyCode === 16 || e.keyCode === 229) {
      this.handleCaret({ isShiftKey: true });
    }
    this.onChange();
  };
  // 计算当前纯文本长度
  getTextLength = () => {
    let dom = this.getCommentRef();
    let length = (dom && dom.innerText.length) || 0;
    return length;
  };

  // 有输入内容
  judgeHasContent = () => {
    return this.getTextLength() === 0;
  };

  // 获取当前的输入内容
  getFinallyContent = () => {
    let { valueKey, labelKey } = this.props;
    let commentDom = this.getCommentRef();
    if (!commentDom) return;
    let innerText = commentDom.innerText;
    let innerHTML = commentDom.innerHTML;
    const mentionMap = {};
    this.mentionList = this.mentionList.filter((data, index) => {
      let name = ` @${data[labelKey]} `;
      let result = innerText.indexOf(name) !== -1;
      if (!mentionMap[data[valueKey]] && result) {
        mentionMap[data[valueKey]] = true;
        return true;
      }
      return false;
    });
    let mentionData = {
      html: commentDom.innerHTML || "",
      text: trim(commentDom.innerText) || "",
      mentionList: this.mentionList,
    };
    return mentionData;
  };

  closeSelector = () => {
    this.needSearch = false;
    // 延迟关闭，解决点击选项时，会先触发blur事件导致选不到人的问题
    setTimeout(() => {
      this.setState({
        showSelector: false,
      });
    }, 300);
  };

  onCommentBlur = () => {
    this.isFocusing = false;
    setTimeout(() => {
      this.isFocusing = false;
    }, 100);
  };

  onCommentFocus = () => {
    setTimeout(() => {
      this.isFocusing = true;
    }, 200);
  };

  onCommentClick = (e) => {
    this.closeSelector();
    this.getCommentRef().focus();
    if (!this.isFocusing) {
      setTimeout(() => {
        const sel = this.getSelection();
        let focusNode = sel.focusNode;
        let textLength = focusNode.length;
        let focusOffset = sel.focusOffset;
        if (focusOffset === 0 || focusOffset === textLength) {
          this.isFocusing = true;
          this.insertNodeToRange(document.createTextNode(""), true);
        } else {
          this.caretBackup = this.getSelection().getRangeAt(0);
        }
      }, 0);
    }
  };

  onChange = () => {
    let { onChange } = this.props;
    if (onChange) {
      onChange(this.getFinallyContent());
    }
  };

  insertNodeToRange = (node, isAutoInsert) => {
    let commentDom = this.getCommentRef();
    let scrollTop = commentDom.scrollTop;
    commentDom.focus();
    let sel = this.getSelection();
    let range = sel.getRangeAt(0);
    if (!isAutoInsert && this.caretBackup) {
      range = this.caretBackup;
    }
    this.caretBackup = range;
    range.insertNode(node);
    range.collapse(false);
    //下面这两行才能让setStartAfter起到作用
    sel.removeAllRanges();
    sel.addRange(range);
    setTimeout(() => {
      commentDom.focus();
      commentDom.scrollTop = scrollTop;
      this.onChange();
    }, 10);
  };

  onItemSelected = (item) => {
    let { labelKey, valueKey, maxSize } = this.props;
    let label = item[labelKey] || "";
    if (label.length + this.getTextLength() > maxSize) {
      this.showMaxSizeTip();
      return;
    }
    this.mentionList.push({
      [labelKey]: label,
      [valueKey]: item[valueKey],
    });
    let commentDom = this.getCommentRef();
    let spanNode = document.createElement("span");
    spanNode.className = "at-text";
    spanNode.innerText = ` @${label}`;
    spanNode.setAttribute("contentEditable", false);
    this.insertNodeToRange(spanNode);
    if (this.textNode) {
      let nodeValue = this.textNode.nodeValue;
      nodeValue = nodeValue.replace(new RegExp(`@${this.keyWord}$`), "");
      this.textNode.nodeValue = nodeValue;
    }
    this.insertNodeToRange(document.createTextNode(" "));
    commentDom.focus();
  };

  DataList = ({ visiable, position }) => {
    if (!visiable) return null;
    let { isLoading, dataList } = this.state;
    let { labelListKeys } = this.props;
    return (
      <div className={"data-list-container"} style={{ ...position }}>
        <Spin spinning={isLoading}>
          {dataList &&
            dataList.map((data) => {
              let label = labelListKeys.reduce((result, item) => result.concat(data[item]), []).join("-");
              return (
                <Popover content={label} placement={"right"}>
                  <div className={"data-list-item text-ellipsis"} onClick={() => this.onItemSelected(data)}>
                    <span>{label}</span>
                  </div>
                </Popover>
              );
            })}
        </Spin>
      </div>
    );
  };

  render() {
    const { DataList } = this;
    const { placeholder, companyOIDs, tip, maxSize } = this.props;
    const { showSelector, showPlaceHolder, content, caretPos } = this.state;
    return (
      <div>
        <div className="mention-block">
          <div className={"comment-container"} onClick={(e) => e.preventDefault() && e.stopPropagation()}>
            <div
              key={"comment-block"}
              id="comment-block"
              className="comment-block"
              contentEditable="true"
              suppressContentEditableWarning="true" //去除控制台warning的显示
              ref={"comment"}
              onFocus={this.onCommentFocus}
              onBlur={this.onCommentBlur}
              onClick={this.onCommentClick}
            />
            <DataList visiable={showSelector} position={caretPos} />
          </div>
          {showPlaceHolder && <div className="mentionPlaceholder">{placeholder}</div>}
          <div className={"comment-info"} style={{ marginTop: "5px" }}>
            <div span={22} className={"notice-tip-wrapper"}>
              <Button type="link" onClick={() => this.insertText("@")} className={"notice-btn"}>
                @{messages("common-1.key1392") /*选人通知*/}
              </Button>
              <span className={"notice-tip"}>{tip}</span>
            </div>
            <div span={2} className={"notice-size"}>
              {this.getTextLength()} / {maxSize}
            </div>
          </div>
        </div>
      </div>
    );
  }

  static propTypes = {
    value: React.PropTypes.oneOfType(React.PropTypes.string, React.PropTypes.object),
    onChange: React.PropTypes.func,
    placeholder: React.PropTypes.string,
    labelKey: React.PropTypes.string,
    valueKey: React.PropTypes.string,
    labelListKeys: React.PropTypes.array, // 列表项要展示的数据格式
    service: React.PropTypes.func, // 提供自定义的service，需配套提供 labelKey 和 ValueKey
    tip: React.PropTypes.string, // 组件提示
    maxSize: React.PropTypes.number, // 组件提示
  };

  static defaultProps = {
    onChange: () => {},
    placeholder: messages("common.please.input") /*请输入*/,
    tip: messages("common-1.key1393") /*默认通知制单人及申请人；若需通知其他人员，请先@指定对象，则仅通知被@的用户*/,
    maxSize: 500,
  };
}

export default Comment;
